#include "gl_protocol.h"
#include "string.h"

static PARSE_RETURN parse_to_frame_struct(PARSE_STRUCT *parse_s);
static int check_frame(uint8_t *frame_buffer, uint16_t frame_len);

PARSE_RETURN parse_struct_init(PARSE_STRUCT *parse_s)
{
    parse_s->sta = NOHEAD;
    parse_s->frame_cnt = 0;
    parse_s->frame_data_len = 0;
    parse_s->frame_len = 0;
    parse_s->version = 0;
    parse_s->rec_frame_cb = NULL;

    return PARSE_OK;
}

void parse_set_send_fun(PARSE_STRUCT *parse_s, send_frame_fun send_frame_fun)
{
    parse_s->frame_s.send_frame_fun = send_frame_fun;
}

void frame_set_send_fun(FRAME_STRUCT *frame_s, send_frame_fun send_frame_fun)
{
    frame_s->send_frame_fun = send_frame_fun;
}

void parse_set_rec_callback(PARSE_STRUCT *parse_s, rec_frame_callback rec_frame_cb)
{
    parse_s->rec_frame_cb = rec_frame_cb;
}

static void reset_frame(PARSE_STRUCT *parse_s, uint8_t recv)
{
    if (recv == HEAD_G)
    {
        parse_s->sta = GETHEADHALF;
    }
    else
    {
        parse_s->sta = NOHEAD;
    }
}

void parse_data(PARSE_STRUCT *parse_s, uint8_t *receive_buffer, uint16_t receive_len)
{
    uint16_t data_cnt = 0;
    for (; data_cnt < receive_len; data_cnt++)
    {
        switch (parse_s->sta)
        {

        case NOHEAD:
            if (receive_buffer[data_cnt] == HEAD_G)
            {
                parse_s->sta = GETHEADHALF;
            }
            break;
        case GETHEADHALF:
            if (receive_buffer[data_cnt] == HEAD_L)
            {
                parse_s->sta = GETHEAD;
                parse_s->frame_cnt = 0;
                parse_s->frame_data_len = 0;
                parse_s->version = 0;
            }
            else
            {
                reset_frame(parse_s, receive_buffer[data_cnt]);
            }
            break;
        case GETHEAD:
            if (receive_buffer[data_cnt] == PROTOCOL_VERSION)
            {
                parse_s->sta = GETVERSION;
                parse_s->version = PROTOCOL_VERSION;
            }
            else
            {
                reset_frame(parse_s, receive_buffer[data_cnt]);
            }
            break;
        case GETVERSION:
            parse_s->frame_data_len = receive_buffer[data_cnt];
            parse_s->sta = GETLENHALF;
            break;
        case GETLENHALF:
            parse_s->frame_data_len |= (uint16_t)(receive_buffer[data_cnt] << 8);

            if (((parse_s->frame_data_len + PROTOCOL_VERSION_OTHER_LEN) <= sizeof(parse_s->frame_buffer)) &&
                (parse_s->frame_data_len <= FRAME_DATA_MAX_LEN))
            {
                *(parse_s->frame_buffer + 0) = HEAD_G;
                *(parse_s->frame_buffer + 1) = HEAD_L;
                *(parse_s->frame_buffer + 2) = PROTOCOL_VERSION;
                *(parse_s->frame_buffer + 3) = (uint8_t)(parse_s->frame_data_len);
                *(parse_s->frame_buffer + 4) = receive_buffer[data_cnt];

                parse_s->sta = GETLEN;
            }
            else
            {
                reset_frame(parse_s, receive_buffer[data_cnt]);
            }

            break;
        case GETLEN:
            *(parse_s->frame_buffer + 5 + parse_s->frame_cnt) = receive_buffer[data_cnt];
            parse_s->frame_cnt++;

            if (parse_s->frame_cnt >= parse_s->frame_data_len + 4)
            {
                if (check_frame(parse_s->frame_buffer + 2, parse_s->frame_data_len + 7) == 0)
                {
                    parse_s->frame_len = parse_s->frame_data_len + PROTOCOL_VERSION_OTHER_LEN;
                    parse_to_frame_struct(parse_s);

                    if (parse_s->rec_frame_cb != NULL)
                    {
                        parse_s->rec_frame_cb(NULL);
                    }

                    parse_s->sta = NOHEAD;
                }
                else
                {
                    reset_frame(parse_s, receive_buffer[data_cnt]);
                }
            }
            break;

        default:
            reset_frame(parse_s, receive_buffer[data_cnt]);
            break;
        }
    }
}

static int check_frame(uint8_t *frame_buffer, uint16_t frame_len)
{
    uint16_t frame_cnt = 0;
    uint16_t check_sum = 0;
    uint8_t check_sum_low = 0, check_sum_high = 0;

    for (frame_cnt = 0; frame_cnt < frame_len - 2; frame_cnt++)
    {
        check_sum += frame_buffer[frame_cnt];
    }

    check_sum = ~check_sum;

    check_sum_low = (uint8_t)check_sum;
    check_sum_high = (uint8_t)(check_sum >> 8);

    if (check_sum_low == frame_buffer[frame_len - 2] && check_sum_high == frame_buffer[frame_len - 1])
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

static PARSE_RETURN parse_to_frame_struct(PARSE_STRUCT *parse_s)
{
    uint16_t count = 5;

    parse_s->frame_s.Version = parse_s->version;
    parse_s->frame_s.FrameDataLen = parse_s->frame_data_len;

    parse_s->frame_s.MasterCmd = parse_s->frame_buffer[count++];
    parse_s->frame_s.SlaveCmd = parse_s->frame_buffer[count++];

    parse_s->frame_s.frame_data = parse_s->frame_buffer + count;

    return PARSE_OK;
}

static PARSE_RETURN send_frame_creat(FRAME_STRUCT *frame_s, uint8_t *frame_buffer, uint16_t *length)
{
    uint16_t check_sum = 0;
    uint16_t temp_cnt = 2;
    uint16_t count = 0;
    uint16_t frame_flag;

    if (frame_s->FrameDataLen > FRAME_DATA_MAX_LEN)
    {
        return PARSE_FAIL;
    }

    frame_buffer[count++] = HEAD_G;
    frame_buffer[count++] = HEAD_L;
    frame_buffer[count++] = frame_s->Version;

    frame_buffer[count++] = (uint8_t)frame_s->FrameDataLen;
    frame_buffer[count++] = (uint8_t)(frame_s->FrameDataLen >> 8);

    frame_buffer[count++] = frame_s->MasterCmd;
    frame_buffer[count++] = frame_s->SlaveCmd;

    memcpy(frame_buffer + count, frame_s->frame_data, frame_s->FrameDataLen);

    frame_flag = frame_s->FrameDataLen + 7;

    for (temp_cnt = 2; temp_cnt < frame_flag; temp_cnt++)
    {
        check_sum += frame_buffer[temp_cnt];
    }

    check_sum = ~check_sum;

    frame_buffer[frame_flag++] = (uint8_t)check_sum;
    frame_buffer[frame_flag++] = (uint8_t)(check_sum >> 8);

    *length = frame_flag;
    return PARSE_OK;
}

PARSE_RETURN creat_send_cmd(FRAME_STRUCT *frame_s)
{
    uint16_t frame_len = 0;
    uint8_t frame_buffer[FRAME_ALL_MAX_LEN];

    if (send_frame_creat(frame_s, frame_buffer, &frame_len) != PARSE_OK)
    {
        return PARSE_FAIL;
    }

    if (frame_s->send_frame_fun != NULL && frame_len > 0)
    {
        frame_s->send_frame_fun(frame_buffer, frame_len);
    }

    return PARSE_OK;
}
